/*  Simple SUIF Interface Definitions */

/*  Copyright (c) 1994 Stanford University

    All rights reserved.

    This software is provided under the terms described in
    the "suif_copyright.h" include file. */

#include <suif_copyright.h>

#ifndef SIMPLE_H
#define SIMPLE_H

/*  Simple SUIF presents a C interface to the SUIF compiler system.  Much
    of the complexity (as well as the power) of SUIF is hidden behind this
    interface.  While Simple SUIF may be inadequate for complicated analyses
    and transformations, it is ideally suited for back-end optimizations
    such as common subexpression elimination and register allocation.  */

#ifndef SUIF_H
typedef int boolean;
#define TRUE 1
#define FALSE 0
#endif



/*  SIMPLE_TYPE:

    The simple_type structure records the types for SUIF variables and for
    the results of SUIF instructions.  The "base" field specifies the
    fundamental kind of type and the "len" field gives the number of bits.
    The "suif_type" is a pointer to the underlying SUIF representation (you
    can ignore this field).  Because the SUIF types contain more information
    than the simple_types, you cannot create new types or modify existing
    ones.  Each type is represented by a unique simple_type instance; i.e.
    you can determine if two types are the same by comparing the addresses
    of the simple_type structures (comparing the "base" and "len" is not
    good enough).  Pointers to some commonly used types are also declared
    here.  */

typedef enum simple_type_base_enum {
    VOID_TYPE,				/* void types */
    SIGNED_TYPE,			/* signed integers */
    UNSIGNED_TYPE,			/* unsigned integers */
    FLOAT_TYPE,				/* floats and doubles */
    ADDRESS_TYPE,			/* pointers */
    RECORD_TYPE				/* structs and unions */
} simple_type_base;

typedef struct simple_type_struct {
    simple_type_base base;		/* base type */
    int len;				/* number of bits */
    void *suif_type;			/* underlying SUIF representation */
} simple_type;

extern simple_type *simple_type_void;
extern simple_type *simple_type_char;
extern simple_type *simple_type_signed;
extern simple_type *simple_type_unsigned;
extern simple_type *simple_type_float;
extern simple_type *simple_type_double;
extern simple_type *simple_type_address;



/*  SIMPLE_SYM:

    Symbols are used to refer to variables, labels, and procedures.  The
    "name" field is a printable string that may not be unique.  The "suif_sym"
    field is a pointer to the underlying SUIF symbol representation (you can
    ignore this field).  The "kind" field identifies the kind of the symbol.
    If a symbol is a variable, the "type" field gives the SUIF type of the
    variable.  Each symbol is represented by a unique simple_sym instance;
    i.e. you can determine if two symbols are the same by comparing the
    addresses of the simple_sym structures. */

typedef enum sym_kind_enum {
    VAR_SYM,				/* variable */
    LABEL_SYM,				/* label */
    PROC_SYM				/* procedure */
} sym_kind;

typedef struct simple_sym_struct {
    sym_kind kind;			/* symbol kind */
    char *name;				/* printable string */
    simple_type *type;			/* variable type (VAR_SYMs only) */
    void *suif_sym;			/* underlying SUIF representation */
} simple_sym;



/*  SIMPLE_REG:

    The operands of SUIF instructions are contained in registers.  These
    registers are divided into several classes:  pseudo registers, temporary
    registers, and machine registers.  Each register has a number.  Pseudo
    and temporary registers share the same "name" space for register numbers,
    but machine registers have a separate "name" space.  In other words,
    a pseudo register and a temporary register will never have the same
    number, but the same number may be used for a machine register and a
    pseudo or temporary register.  As with symbols, each register is
    represented by a unique simple_reg instance, so internally you can just
    refer to registers by the addresses of their simple_reg structures.  The
    "var" field identifies the variables stored in pseudo and temporary
    registers; it is not used for machine registers.  */

typedef enum reg_kind_enum {
    PSEUDO_REG,				/* pseudo register */
    TEMP_REG,				/* temporary register */
    MACHINE_REG				/* machine register */
} reg_kind;

typedef struct simple_reg_struct {
    reg_kind kind;			/* kind of register */
    int num;				/* register number */
    simple_sym *var;			/* variable held in the register */
} simple_reg;

#ifndef NO_REGISTER
#define NO_REGISTER ((simple_reg *)0)
#endif



/*  MACHINE_REGS:

    The machine registers are divided into general-purpose (GPR) and
    floating-point (FPR) registers.  Each of these groups is further divided
    into callee-saved and caller-saved registers.  The values in callee-saved
    registers are preserved across procedures but you have to explicitly save
    the caller-saved registers at each call site where they are live.  The
    machine registers are only needed if you are doing register allocation.  */

#define CALLEE_GPRS 0			/* callee-saved, general-purpose */
#define CALLER_GPRS 1			/* caller-saved, general-purpose */
#define CALLEE_FPRS 2			/* callee-saved, floating-point */
#define CALLER_FPRS 3			/* caller-saved, floating-point */
#define MACHINE_REG_KINDS 4		/* number of machine register kinds */

extern int num_machine_regs[];		/* number of each type of register */
extern simple_reg **machine_regs[];	/* arrays of simple_regs */



/*  SIMPLE_IMMED:

    The immed structure represents various immediate constants used by
    the LDC (load constant) instructions.  An immediate value may be
    an integer, a floating point value, or a symbolic address.  Addresses
    consist of a symbol and a constant offset.  */

typedef enum immed_type_enum {
    IMMED_INT,				/* integer constant */
    IMMED_FLOAT,			/* floating-point constant */
    IMMED_SYMBOL			/* symbolic address */
} immed_type;

typedef struct simple_immed_struct {
    immed_type format;			/* format tag */
    union {
	int ival;			/* integer value */
	double fval;			/* floating-point value */
	struct {
	    simple_sym *symbol;		/* symbolic address */
	    int offset;			/* offset from the symbol */
	} s;
    } u;
} simple_immed;



/*  SIMPLE_INSTR:

    The simple_instr structure holds an instruction on a doubly linked list
    of instructions.  All of the instructions have an opcode, a type field,
    and pointers to the next and previous instructions.  For each format,
    there are additional fields that are specific to instructions of that
    format.  */

/*  read the opcodes */
typedef enum simple_op_enum {
#define SOP(OPCODE, NAME, FORM, SUIFOP) OPCODE ,
#include "simple_ops.def"
    LAST_OP
} simple_op;

typedef enum simple_format_enum {
    BASE_FORM,				/* most instructions */
    BJ_FORM,				/* branch or jump */
    LDC_FORM,				/* load constant */
    CALL_FORM,				/* call */
    MBR_FORM,				/* multi-way branch */
    LABEL_FORM				/* label */
} simple_format;

typedef struct simple_instr_struct {
    simple_op opcode;			/* the opcode */
    simple_type *type;			/* type of the result */
    struct simple_instr_struct *next;	/* ptr to next instruction */
    struct simple_instr_struct *prev;	/* ptr to previous instruction */

    /* the variant part is selected by simple_op_format(opcode) */
    union {

	/* BASE_FORM */
	struct {
	    simple_reg *dst;		/* destination */
	    simple_reg *src1;		/* source 1 */
	    simple_reg *src2;		/* source 2 */
	} base;

	/* BJ_FORM */
	struct {
	    simple_sym *target;		/* branch target label */
	    simple_reg *src;		/* source register */
	} bj;

	/* LDC_FORM */
	struct {
	    simple_reg *dst;		/* destination */
	    simple_immed value;		/* immediate constant */
	} ldc;

	/* CALL_FORM */
	struct {
	    simple_reg *dst;		/* return value destination */
	    simple_reg *proc;		/* address of the callee */
	    unsigned nargs;		/* number of arguments */
	    simple_reg **args;		/* array of arguments */
	} call;

	/* MBR_FORM */
	struct {
	    simple_reg *src;		/* branch selector */
	    int offset;			/* branch selector offset */
	    simple_sym *deflab;		/* label of default target */
	    unsigned ntargets;		/* number of possible targets */
	    simple_sym **targets;	/* array of labels */
	} mbr;

	/* LABEL_FORM */
	struct {
	    simple_sym *lab;		/* the symbol for this label */
	} label;
    } u;
} simple_instr;



/*  LIBRARY FUNCTIONS:

    new_instr		allocate and initialize a simple_instr 
    free_instr		deallocate a simple_instr
    new_register	allocate a new register
    new_label		allocate a new label
    get_ptr_type	get a type that is a pointer to another type
    simple_op_name	return the name of an opcode
    simple_op_format	return the format for an opcode
*/

extern simple_instr *new_instr(simple_op op, simple_type *t);
extern void free_instr(simple_instr *s);
extern simple_reg *new_register(simple_type *t, reg_kind k);
extern simple_sym *new_label();
extern simple_type *get_ptr_type(simple_type *t);
extern char *simple_op_name(simple_op o);
extern simple_format simple_op_format(simple_op o);

/*  error functions */
#define simple_error(msg) _simple_error(msg, __FILE__, __LINE__)
#define simple_warning(msg) _simple_warning(msg, __FILE__, __LINE__)
extern void _simple_error(char *msg, char *file, int line);
extern void _simple_warning(char *msg, char *file, int line);

/*  user-provided function to analyze and optimize a procedure */
extern simple_instr *do_procedure(simple_instr *inlist, char *proc_name);

#endif /* SIMPLE_H */
